/**
 * 广寒宫
 * 网址:www.guanghangong.xyz
 */
package com.moon.admin.framework.springmvc;


import lombok.extern.slf4j.Slf4j;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.moon.admin.framework.springmvc.domain.R;
import com.moon.admin.framework.springmvc.domain.RCode;
import com.moon.admin.framework.util.MoonException;

/**
 * 错误处理
 * 
 * @author ninglong
 */
@Configuration
@Slf4j
@RestControllerAdvice
@ConditionalOnWebApplication
public class AdviceErrorAutoConfiguration {

	@ConditionalOnClass(MethodArgumentNotValidException.class)
	@Configuration
	@RestControllerAdvice
	static class MethodArgumentNotValidExceptionConfiguration {
		@SuppressWarnings("rawtypes")
		@ResponseStatus(value = HttpStatus.BAD_REQUEST)
		@ExceptionHandler(MethodArgumentNotValidException.class)
		// 验证requestbody失败异常的处理
		public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
			logError("methodArgumentNotValidException", e.getMessage(), e);
			return R.fail(RCode.FAIL.getCode(),
					MethodArgumentNotValidExceptionHelper.firstErrorMessage(e.getBindingResult()));
		}

		private static class MethodArgumentNotValidExceptionHelper {
			static String firstErrorMessage(BindingResult bindingResult) {
				return bindingResult.getAllErrors().stream().findFirst()
						.map(ObjectError::getDefaultMessage).orElse("");
			}
		}
	}

	@ConditionalOnClass(MissingServletRequestParameterException.class)
	@Configuration
	@RestControllerAdvice
	// 对于接口需要参数，但是没有传参数的异常处理逻辑
	static class MissingServletRequestParameterExceptionConfiguration {
		@SuppressWarnings("rawtypes")
		@ResponseStatus(value = HttpStatus.BAD_REQUEST)
		@ExceptionHandler(MissingServletRequestParameterException.class)
		public R handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
			logError("missingServletRequestParameterException", e.getMessage(),e);
			return R.fail(
					RCode.FAIL.getCode(),
					String.format("参数%s未传", e.getParameterName()));
		}
	}

	@ConditionalOnClass(HttpMediaTypeNotSupportedException.class)
	@Configuration
	@RestControllerAdvice
	// 对于传入的媒体类型不正确的异常处理
	static class HttpMediaTypeNotSupportedExceptionConfiguration {
		@SuppressWarnings("rawtypes")
		@ResponseStatus(value = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
		@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
		public R handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
			logError("httpMediaTypeNotSupportedException", e.getMessage(), e);
			return R.fail(
					RCode.FAIL.getCode(),
					String.format("媒体类型%s错误", e.getContentType()));
		}
	}

	@ConditionalOnClass(HttpRequestMethodNotSupportedException.class)
	@Configuration
	@RestControllerAdvice
	static class HttpRequestMethodNotSupportedExceptionConfiguration {
		@SuppressWarnings("rawtypes")
		@ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
		@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
		// 对于接口方法不匹配的异常处理
		public R handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
			logError("httpRequestMethodNotSupportedException", e.getMessage(),e);
			return R.fail(
					RCode.FAIL.getCode(),
					e.getMessage());
		}
	}

	@ConditionalOnClass(Exception.class)
	@Configuration
	@RestControllerAdvice
	static class MoonCommonExceptionConfiguration {
		@SuppressWarnings("rawtypes")
		@ResponseStatus(value = HttpStatus.OK)
		@ExceptionHandler(MoonException.class)
		// 对于接口自定义异常的处理
		public R handleLocException(MoonException e) {
			logError("moonException", e.getMessage(), e);
			return R.fail(e.getCode(), e.getMsg());
		}
	}

	@SuppressWarnings("rawtypes")
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(RuntimeException.class)
	// 对于接口运行时错误异常的处理
	public R handleRuntimeException(RuntimeException e) {
		logError("runtimeException", e.getMessage(), e);
		return R.fail(RCode.FAIL.getCode(),e.getMessage());
	}

	@SuppressWarnings("rawtypes")
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(Throwable.class)
	// 对于所有异常的处理
	public R handleThrowable(Throwable t) {
		logError("throwableError", t.getMessage(), t);
		return R.fail(RCode.FAIL.getCode(),t.getMessage());
	}

	static void logError(String name, String msg, Throwable t) {
		log.error("error name is {}, msg is {}", name, msg, t);
	}
}